using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using BulletOnRails4NET.Models;
using System.Reflection;
using System.Linq.Dynamic;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using MSTeachingGrammar.Models;
using Suteki.Common.Repositories;

namespace BulletOnRails4NET.Controllers
{
    public abstract class NonAjaxScaffoldController<CModelType> : Controller where CModelType : CrudModel
    {
        protected IRepository<CModelType> _db;
        private Type _db_type;
       
        protected int _admin_clubber_id = 1;
        private ConstructorInfo _model_constructor;
        //private MethodInfo _db_get_method, 
        //    _db_save_method, 
        //    _db_delete_method;
           //d _db_find_all_method;

        //Associations (belongs to)
        private Dictionary<string, MethodInfo> _associations_list_getters;
        List<AssociationQueryInfo> _associations_info;

        private PropertyInfo[] _crud_model_properties;
        public  GridHelper Grid {get; set;}
        const int _page_size = 5;

        public NonAjaxScaffoldController(string model_name)
        {
            _db = new Repository<CModelType>();
            _db_type = _db.GetType();
            initialize(model_name, typeof(CModelType));

        }
        public NonAjaxScaffoldController(string model_name, IRepository<CModelType> repository)
        {
            _db = repository;
            initialize(model_name, typeof(CModelType));
        }

        private void initialize(string model_name, Type model_type)
        {
            _db_type = _db.GetType();
            _model_constructor = model_type.GetConstructor(System.Type.EmptyTypes);
           // _db_get_method = _db_type.GetMethod("Get" + model_name);
           // _db_save_method = _db_type.GetMethod("Add", new Type[] { model_type });
           // _db_delete_method = _db_type.GetMethod("Delete", new Type[] { model_type });
            _crud_model_properties = GetDisplayableModelProperties();
          //  _db_find_all_method = _db_type.GetMethod("FindAll" + model_name + "Records");
            string controller_name = GetType().Name.Replace("Controller", "");
            Grid = new GridHelper(_crud_model_properties, 
                PathResolver.GetSiteRoot() + controller_name + "/GetGridData", 
                GetPageSize(), controller_name,
                PathResolver.GetSiteRoot() + controller_name + "/GetGridMetaData");
            BuildAssociationsListGetters();
            AddCustomFields();
        }

        protected virtual void AddCustomFields()
        {
        }

        //Builds the lists that contain the queries to the "belong-to"
        // associations contained in the CrudModel 
        protected virtual void BuildAssociationsListGetters()
        {
            PropertyInfo[] model_properties = typeof(CModelType).GetProperties();
            object[] info;
            AssociationAttribute assoc_attr;
            _associations_list_getters = new Dictionary<string, MethodInfo>();
            _associations_info = new List<AssociationQueryInfo>();
            MethodInfo curr_assoc_find_all; 
            foreach (PropertyInfo prop in model_properties)
            {
                //Get the Linq related info
                info = prop.GetCustomAttributes(typeof(AssociationAttribute), true);
                // Check if it is a Linq column property
                if (info.Length == 1)
                {
                    assoc_attr = (AssociationAttribute)info[0];
                    if (assoc_attr.IsForeignKey)
                    {
                        curr_assoc_find_all = _db_type.GetMethod("FindAll" + prop.PropertyType.Name + "Records");
                        _associations_list_getters.Add(assoc_attr.ThisKey, curr_assoc_find_all);
                        IQueryable<CrudModel> possible_assoc_items = (IQueryable<CrudModel>)curr_assoc_find_all.Invoke(_db, null);
                        _associations_info.Add(new AssociationQueryInfo(assoc_attr.ThisKey, possible_assoc_items));
                    }
                 }
            }
        }


        //Gets the Model's properties that can be edited and shown by the scaffold
        protected virtual PropertyInfo[] GetDisplayableModelProperties()
        {
            PropertyInfo[] model_properties = typeof(CModelType).GetProperties();
            object[] info;
            List<PropertyInfo> db_properties = new List<PropertyInfo>();
            foreach (PropertyInfo prop in model_properties)
            {
                //Get the Linq related info
                info = prop.GetCustomAttributes(typeof(ColumnAttribute), true);
                // Check if it is a Linq column property
                if (info.Length == 1)
                {
                    db_properties.Add(prop);
                }
            }
            return db_properties.ToArray();
        }

        public virtual ActionResult Index()
        {
            return RedirectToAction("List");
        }
        
        public virtual ActionResult List()
        {
            return View(Grid);
        }

        public virtual ActionResult GetGridMetaData()
        {
            return Grid.grid_meta_data;
        }

        public virtual ActionResult GetGridData()
        {

            //int page_number = 0;
            string sort_field = Request["sidx"];
            string sort_order = Request["sord"];
            if (sort_order == "desc")
                sort_order = "descending";
            else if (sort_order == "asc")
                sort_order = "";
            int page_number = Convert.ToInt32(Request["page"]);
            if (page_number > 0)
                page_number--;
            else
                page_number = 0;
          
            IQueryable<CModelType> clubbers = (IQueryable<CModelType>) _db.GetAll() ;

            var rows = (clubbers.OrderBy(sort_field + " " + sort_order).Skip(page_number * GetPageSize())
.Take(GetPageSize()).Select(c =>
        
        new
            {

                id = c.GetId(),

                cell = c.GetPropertyValuesAsString(_crud_model_properties)

            }
    )).ToArray();

            int num_clubbers = clubbers.Count();
            return new JsonResult
            {
                Data = new
                {
                    page = page_number + 1,
                    records = rows.Length,
                    rows,
                    total = (int)Math.Ceiling(num_clubbers / (double)GetPageSize())
                }
            };

            
        }

        public virtual IQueryable<CModelType> GetRecordsForListView()
        {
            return null;
        }

        public virtual int GetPageSize()
        {
            return _page_size;
        }
        //
        // GET: /Clubber/Details/5

        public virtual ActionResult Details(int id)
        {
            CrudModel clubber = (CrudModel)_db.GetById(id);
            if (clubber == null)
                return View("NotFound");
            return View(new CrudViewModel(clubber, GetDisplayableModelProperties()));
        }

        [OutputCache(Duration = 3600 * 24, VaryByParam = "none")]
        public virtual ActionResult Create()
        {
            CrudModel new_clubber = (CrudModel)_model_constructor.Invoke(null);
            return View(new CrudViewModel(new_clubber, GetDisplayableModelProperties(), _associations_info));
        }

        protected virtual List<RuleViolation> BeforeSaveCreate(FormCollection formValues,
            CrudModel model_to_save)
        {
            return null;
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public virtual ActionResult Create(FormCollection formValues)
        {
            CrudModel new_clubber = (CrudModel)_model_constructor.Invoke(null);
            List<RuleViolation> violations = FormHelper.PopulateProperties(new_clubber, formValues);
            if (violations.Count == 0)
            {   
                try
                {
                    //UpdateModel(new_clubber);
                    _db.InsertOnSubmit(new_clubber as CModelType);
                    List<RuleViolation> extra_violations = BeforeSaveCreate(formValues, new_clubber);
                    if (extra_violations == null || extra_violations.Count == 0)
                    {
                        _db.SubmitChanges();
                        return RedirectToAction("Details", new { id = new_clubber.GetId() });
                    }
                    else
                    {
                        ModelState.AddRuleViolations(extra_violations, formValues);
                    }
                }
                catch (RuleViolationException e)
                {
                    ModelState.AddRuleViolations(e.RulesViolations, formValues);
                }
            }
            else {
                ModelState.AddRuleViolations(violations, formValues);
            }
            return View(new CrudViewModel(new_clubber, GetDisplayableModelProperties(), _associations_info));
        }

        ////
        //// GET: /Exercises/Edit/5
        //[Authorize(Roles = "admin")]
        public virtual ActionResult Edit(int id)
        {
            CrudModel clubber = (CrudModel)_db.GetById(id);
            if (clubber == null)
                return View("NotFound");
            return View(new CrudViewModel(clubber, GetDisplayableModelProperties(), _associations_info));
        } 

        ////
        // POST: /Exercises/Edit/5
        [AcceptVerbs(HttpVerbs.Post)]
        public virtual ActionResult Edit(int id, FormCollection formValues)
        {
            CrudModel clubber = (CrudModel)_db.GetById(id); 
            if (clubber == null)
                return View("NotFound");
            List<RuleViolation> violations = FormHelper.PopulateProperties(clubber, formValues);
            if (violations.Count == 0)
            {
                try
                {
                    _db.SubmitChanges();
                    return RedirectToAction("Details", new { id = clubber.GetId() });
                }
                catch (RuleViolationException e)
                {
                    ModelState.AddRuleViolations(e.RulesViolations, formValues);
                }
            }
            else
            {
                ModelState.AddRuleViolations(violations, formValues);
            }; 
            return View(new CrudViewModel(clubber, GetDisplayableModelProperties(), _associations_info));
     
        }

        

        public virtual ActionResult Delete(string ids_to_delete)
        {
            string[] ids_to_delete_arr = ids_to_delete.Split(',');
            int id;

            List<CrudModel> clubbers_to_delete = new List<CrudModel>();
            foreach (string str_id in ids_to_delete_arr)
            {
                if (int.TryParse(str_id, out id))
                {
                    CrudModel model = (CrudModel)_db.GetById(id);
                    if (model != null)
                        clubbers_to_delete.Add(model);
                }
            }
            if (clubbers_to_delete.Count > 0)
                return View(clubbers_to_delete);
            else
                return View("NotFound");
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public virtual ActionResult Delete()
        {
            int id;
            var ids = Request.Form;
            List<string> clubbers_deleted = new List<string>();
            foreach (string str_id in ids)
            {
                if (int.TryParse(str_id, out id))
                {
                    CrudModel model = (CrudModel)_db.GetById(id);
                    if (model != null)
                    {
                        _db.DeleteOnSubmit(model as CModelType);
                        clubbers_deleted.Add(model.GetAsLabel());
                    }
                }
            }
            _db.SubmitChanges();
            return View("ConfirmedDelete", clubbers_deleted);
        }

    }
}
